//
// Created by kode.zhong on 2021/3/14.
//

#ifndef LOX_VM_LOX_SCANNER_H_
#define LOX_VM_LOX_SCANNER_H_

#include <string>

namespace lox {

enum TokenType {
  // Single-character tokens.
  TOKEN_LEFT_PAREN, TOKEN_RIGHT_PAREN,
  TOKEN_LEFT_BRACE, TOKEN_RIGHT_BRACE,
  TOKEN_COMMA, TOKEN_DOT, TOKEN_MINUS, TOKEN_PLUS,
  TOKEN_SEMICOLON, TOKEN_SLASH, TOKEN_STAR,

  // One or two character tokens.
  TOKEN_BANG, TOKEN_BANG_EQUAL,
  TOKEN_EQUAL, TOKEN_EQUAL_EQUAL,
  TOKEN_GREATER, TOKEN_GREATER_EQUAL,
  TOKEN_LESS, TOKEN_LESS_EQUAL,

  // Literals.
  TOKEN_IDENTIFIER, TOKEN_STRING, TOKEN_NUMBER,

  // Keywords.
  TOKEN_AND, TOKEN_CLASS, TOKEN_ELSE, TOKEN_FALSE,
  TOKEN_FOR, TOKEN_FUN, TOKEN_IF, TOKEN_NIL, TOKEN_OR,
  TOKEN_PRINT, TOKEN_RETURN, TOKEN_SUPER, TOKEN_THIS,
  TOKEN_TRUE, TOKEN_VAR, TOKEN_WHILE,

  TOKEN_ERROR,
  TOKEN_EOF
};

/**
 * In book: use original source string as character store, which represented by a pointer to its
 * first char and the number of chars it contains.
 * We use a string here to store lexme.
 */
struct Token {
  TokenType type;
  std::string lexme;
  int line;
};

/**
 * Token scanner.
 */
class Scanner {
 public:
  Scanner() {}
  explicit Scanner(const std::string& source);
  std::string m_source;
  std::string::const_iterator m_start;
  std::string::const_iterator m_current;
  size_t m_line;
  Token ScanToken();
 private:
  bool IsAtEnd() const;
  Token MakeToken(TokenType type) const;
  Token ErrorToken(const char* msg) const;
  char Advance();
  bool Match(char expected);
  void SkipWhitSpace();
  char Peek() const;
  char PeekNext();
  Token MakeString();
  Token MakeNumber();
  Token MakeIdentifier();
  TokenType IdentifierType();
  TokenType CheckKeyword(int start, int length, const std::string& rest, TokenType type) const;
};
}

#endif //LOX_VM_LOX_SCANNER_H_
